import org.gradle.BuildListener
import org.gradle.BuildResult
import org.gradle.api.Project
import org.gradle.api.ProjectEvaluationListener
import org.gradle.api.ProjectState
import org.gradle.api.initialization.Settings
import org.gradle.api.invocation.Gradle

class ConfigUtils {

    static addBuildListener(Gradle g) {
        TaskDurationUtils.init(g)

        g.addBuildListener(new BuildListener() {
            @Override
            void buildStarted(Gradle gradle) {
                GLog.d("buildStarted")
            }

            @Override
            void settingsEvaluated(Settings settings) {
                //settings执行的时候
                GLog.d("settingsEvaluated")
                includeModule(settings)
            }

            @Override
            void projectsLoaded(Gradle gradle) {
                //settingsEvaluated执行完成之后会执行到这里
                //在其他的module执行之前会调用
                GLog.d("projectsLoaded")

                //赋值那些dep(需要依赖的东西)
                generateDep(gradle)

                gradle.addProjectEvaluationListener(new ProjectEvaluationListener() {
                    @Override
                    void beforeEvaluate(Project project) {
                        //在每个module的build.gradle执行之前会执行
                        GLog.d("beforeEvaluate")
                        if (project.subprojects.isEmpty()) {
                            //如果module的名称是app,则依赖buildApp.gradle.
                            //免得每次都去module的build.gradle写一些重复apply代码
                            if (project.name == "app") {
                                GLog.l(project.toString() + " applies buildApp.gradle")
                                project.apply {
                                    from "${project.rootDir.path}/buildApp.gradle"
                                }
                            } else {
                                //如果module是lib,则依赖buildLib.gradle
                                GLog.l(project.toString() + " applies buildLib.gradle")
                                project.apply {
                                    from "${project.rootDir.path}/buildLib.gradle"
                                }
                            }
                        }
                    }

                    @Override
                    void afterEvaluate(Project project, ProjectState projectState) {
                        //在每个module的build.gradle执行之后执行
                        GLog.d("afterEvaluate")
                    }
                })
            }

            @Override
            void projectsEvaluated(Gradle gradle) {
                GLog.d("projectsEvaluated")
            }

            @Override
            void buildFinished(BuildResult buildResult) {
                GLog.d("buildFinished")
            }
        })
    }

    /**
     * 根据depConfig生成DepConfig中的dep(需要依赖的东西,可能是project也可能是远程的仓库地址)
     * 因为Config中的depConfig是一个变量,相当于是给这个变量中的那些配置(DepConfig)的dep赋值
     * @param gradle
     */
    private static generateDep(Gradle gradle) {
        def config = getDepConfigByFilter(new DepConfigFilter() {
            @Override
            boolean accept(String name, DepConfig config) {
                if (config.useLocal) {
                    //如果使用的是本地模块,那么把它转化为project
                    config.dep = gradle.rootProject.findProject(config.localPath)
                } else {
                    //如果是远端依赖,那就直接使用远端依赖即可
                    config.dep = config.remotePath
                }
                return true
            }
        })

        GLog.l("generateDep = ${GLog.object2String(config)}")
    }

    /**
     * 在settings.gradle中根据appConfig和pkgConfig来include本地模块
     */
    private static includeModule(Settings settings) {
        /*
        本来是这种样子就可以include的,但是现在换成下面的写法了,易扩展
        settings.include ':lib:base', ':lib:common',
                ':feature:feature0:export', ':feature:feature1:export',
                ':feature:feature0:pkg', ':feature:feature1:pkg',
                ':feature:feature0:app', ':feature:feature1:app',
                ':feature:launcher:app'*/

        //是否应用mock 判断,当pkg全部依赖的时候,就不需要mock
        if (Config.pkgConfig.isEmpty()) {
            Config.depConfig.feature.mock.isApply = false
        }
        def config = getDepConfigByFilter(new DepConfigFilter() {
            @Override
            boolean accept(String name, DepConfig config) {
                //1. 如果是以.app结尾的,说明是app 的module,此时根据Config.appConfig来判断是否需要依赖进来
                //2. 如果是Config.pkgConfig(需要依赖的功能包)不为空,说明是pkg调试模式.筛选已配置的pkgConfig,依赖进来
                //3. 筛选出本地的module(当然得是isApply为true的)
                if (name.endsWith('.app')) {
                    //获取app模块的名字 比如:feature:launcher:app 中的launcher
                    def appName = name.substring('feature.'.length(), name.length() - 4)
                    if (!Config.appConfig.contains(appName)) {
                        config.isApply = false
                    }
                }
                if (!Config.pkgConfig.isEmpty()) {
                    if (name.endsWith('.pkg')) {
                        //获取pkg模块的名字
                        def pkgName = name.substring('feature.'.length(), name.length() - 4)
                        if (!Config.pkgConfig.contains(pkgName)) {
                            config.isApply = false
                        }
                    }
                }

                //过滤出本地并且apply的模块
                if (!config.isApply) return false
                if (!config.useLocal) return false
                if (config.localPath == "") return false
                return true
            }
        }).each { _, cfg ->
            //把本地模块include进去
            settings.include cfg.localPath
        }

        GLog.l("includeModule = ${GLog.object2String(config)}")
    }

    /**
     * 根据过滤器来获取DepConfig
     * @return
     */
    static Map<String, DepConfig> getDepConfigByFilter(DepConfigFilter filter) {
        return _getDepConfigByFilter("", Config.depConfig, filter)
    }

    private static _getDepConfigByFilter(String namePrefix, Map map, DepConfigFilter filter) {
        //结果map
        def depConfigList = [:]
        for (Map.Entry entry : map.entrySet()) {
            def (name, value) = [entry.getKey(), entry.getValue()]
            //如果是value是map的话,则需要递归添加进来
            if (value instanceof Map) {
                //这里的.是随便加的,比如:
                /*
                depConfig中定义了lib,那么加了namePrefix就变成了.lib.base   为了上面做切割
                lib  : [
                            base  : new DepConfig(":lib:base"),
                            common: new DepConfig(":lib:common"),
                        ],
                * */
                namePrefix += (name + '.')
                depConfigList.putAll(_getDepConfigByFilter(namePrefix, value, filter))
                namePrefix -= (name + '.')
                continue
            }

            //符合过滤条件就加到结果里
            def config = value as DepConfig
            if (filter == null || filter.accept(namePrefix + name, config)) {
                depConfigList.put(namePrefix + name, config)
            }
        }
        return depConfigList
    }

    //这里的Map<String, DepConfig>可以省略
    static Map<String, DepConfig> getApplyPkgs() {
        def applyPkgs = getDepConfigByFilter(new DepConfigFilter() {
            @Override
            boolean accept(String name, DepConfig config) {
                if (!config.isApply) return false
                return name.endsWith(".pkg")
            }
        })
        GLog.d("getApplyPkgs = ${GLog.object2String(applyPkgs)}")
        return applyPkgs
    }

    static getApplyExports() {
        def applyExports = getDepConfigByFilter(new DepConfigFilter() {
            @Override
            boolean accept(String name, DepConfig config) {
                if (!config.isApply) return false
                return name.endsWith(".export")
            }
        })
        GLog.d("getApplyExports = ${GLog.object2String(applyExports)}")
        return applyExports
    }

    static getApplyPlugins() {
        def plugins = getDepConfigByFilter(new DepConfigFilter() {
            @Override
            boolean accept(String name, DepConfig config) {
                if (!name.startsWith("plugin.")) return false
                if (!config.isApply) return false
                return true
            }
        })
        GLog.d("getApplyPlugins = ${GLog.object2String(plugins)}")
        return plugins
    }

}

interface DepConfigFilter {
    boolean accept(String name, DepConfig config)
}

/*
setting.gradle
settingsEvaluated
projectsLoaded
root project 'AucFrameTemplate' -> root
project ':lib:base' -> lib
project ':lib:common' -> lib
project ':feature:feature0:app' -> app
project ':feature:feature0:export' -> lib
project ':feature:feature0:pkg' -> lib
project ':feature:feature1:app' -> app
project ':feature:feature1:export' -> lib
project ':feature:feature1:pkg' -> lib
project ':feature:launcher:app' -> app
projectsEvaluated
buildFinished
* */
